package net.oschina.allchat.message.processing.json.parsers.im;

import java.util.ArrayList;
import java.util.List;

import net.oschina.allchat.message.processing.Attribute;
import net.oschina.allchat.message.processing.ElementParserAdaptor;
import net.oschina.allchat.message.processing.IElementParser;
import net.oschina.allchat.message.processing.IParser;
import net.oschina.allchat.message.processing.IParserFactory;
import net.oschina.allchat.message.processing.IParsingContext;
import net.oschina.allchat.message.processing.json.ParsingUtils;
import net.oschina.allchat.message.processing.json.parsers.core.StanzaParser;
import net.oschina.allchat.protocol.HandyUtils;
import net.oschina.allchat.protocol.core.LangText;
import net.oschina.allchat.protocol.core.Protocol;
import net.oschina.allchat.protocol.core.ProtocolException;
import net.oschina.allchat.protocol.core.stanza.error.BadRequest;
import net.oschina.allchat.protocol.im.stanza.Presence;

public class PresenceParserFactory implements IParserFactory<Presence> {
	private static final String VALUE_TYPE_AVAILABLE = "available";
	private static final String VALUE_TYPE_UNAVAILABLE = "unavailable";
	private static final String VALUE_TYPE_SUBSCRIBE = "subscribe";
	private static final String VALUE_TYPE_SUBSCRIBED = "subscribed";
	private static final String VALUE_TYPE_UNSUBSCRIBE = "unsubscribe";
	private static final String VALUE_TYPE_PROBE = "probe";
	
	private static final String VALUE_SHOW_CHAT = "chat";
	private static final String VALUE_SHOW_AWAY = "away";
	private static final String VALUE_SHOW_DND = "dnd";
	private static final String VALUE_SHOW_XA = "xa";
	
	private static final String PATH_PRESENCE_STATUS = "/status";
	private static final String PATH_PRESENCE_STATUS_ITEM = "/status/[]";
    private static final String PATH_PRESENCE_SHOW = "/show";
    private static final String PATH_PRESENCE_PRIORITY = "/priority";
    
    private static final Object KEY_LANG_TEXT = new Object();
	
	@Override
	public Protocol getProtocol() {
		return Presence.PROTOCOL;
	}

	@Override
	public IParser<Presence> create() {
		return new PresenceParser();
	}

	private class PresenceParser extends StanzaParser<Presence> {

		@Override
		public Presence createObject() {
			return new Presence();
		}

		@Override
		protected IElementParser<Presence> doGetElementParser(String parsingPath) {
			if (PATH_PRESENCE_STATUS.equals(parsingPath)) {
				return new ElementParserAdaptor<Presence>() {
					@Override
					public void processAttributes(IParsingContext<Presence> context, List<Attribute<?>> attributes) {
						super.processAttributes(context, attributes);
						List<LangText> status = new ArrayList<LangText>();
						context.getObject().setStatus(status);
					}
				};
			} else if (PATH_PRESENCE_STATUS_ITEM.equals(parsingPath)) {
				return new IElementParser<Presence>() {
					@Override
					public void processText(IParsingContext<Presence> context, String text) {
						if (HandyUtils.isBlank(text)) {
							throw new ProtocolException(new BadRequest("status text is null"));
						}
						
						LangText langText = context.removeAttribute(KEY_LANG_TEXT);
						if (langText == null) {
							throw new RuntimeException("null LangText???");
						}
						
						langText.setText(text);
					}
					
					@Override
					public void processAttributes(IParsingContext<Presence> context, List<Attribute<?>> attributes) {
						LangText langText = new LangText(null, ParsingUtils.processLangTextAttributes(attributes));
						context.getObject().getStatus().add(langText);
						context.setAttribute(KEY_LANG_TEXT, langText);
					}
				};
			} else if (PATH_PRESENCE_SHOW.equals(parsingPath)) {
				return new ElementParserAdaptor<Presence>() {
					@Override
					public void processText(IParsingContext<Presence> context, String text) {
						if (VALUE_SHOW_CHAT.equals(text)) {
							context.getObject().setShow(Presence.Show.CHAT);
						} else if (VALUE_SHOW_AWAY.equals(text)) {
							context.getObject().setShow(Presence.Show.AWAY);
						} else if (VALUE_SHOW_DND.equals(text)) {
							context.getObject().setShow(Presence.Show.DND);
						} else if (VALUE_SHOW_XA.equals(text)) {
							context.getObject().setShow(Presence.Show.XA);
						} else {
							throw new ProtocolException(new BadRequest(String.format("invalid presence show '%s'")));
						}
					}
				};
			} else if (PATH_PRESENCE_PRIORITY.equals(parsingPath)) {
				return new ElementParserAdaptor<Presence>() {
					@Override
					public void processText(IParsingContext<Presence> context, String text) {
						if (context.getObject().getPriority() != null) {
							throw new ProtocolException(new BadRequest("reduplicated 'priority'"));
						}
						
						try {
							context.getObject().setPriority(Integer.valueOf(text));
						} catch (NumberFormatException e) {
							throw new ProtocolException(new BadRequest("value of 'priority' must be an integer"));
						}
						
						// (rfc3921 2.2.2.3)
						if (context.getObject().getPriority() < -128 || context.getObject().getPriority() > 127) {
							throw new ProtocolException(new BadRequest("value of 'priority' must be between -128 and 127"));
						}
					}
				};
			} else {
				return null;
			}
		}

		@Override
		protected void processType(IParsingContext<Presence> context, String value) {
			if (VALUE_TYPE_AVAILABLE.equals(value)) {
				context.getObject().setType(Presence.Type.AVAILABLE);
			} else if (VALUE_TYPE_UNAVAILABLE.equals(value)) {
				context.getObject().setType(Presence.Type.UNAVAILABLE);
			} else if (VALUE_TYPE_SUBSCRIBE.equals(value)) {
				context.getObject().setType(Presence.Type.SUBSCRIBE);
			} else if (VALUE_TYPE_SUBSCRIBED.equals(value)) {
				context.getObject().setType(Presence.Type.SUBSCRIBED);
			} else if (VALUE_TYPE_UNSUBSCRIBE.equals(value)) {
				context.getObject().setType(Presence.Type.UNSUBSCRIBE);
			} else if (VALUE_TYPE_PROBE.equals(value)) {
				context.getObject().setType(Presence.Type.PROBE);
			} else {
				throw new ProtocolException(new BadRequest(String.format("invalid presence type '%s'", value)));
			}
		}
	}
}
